#include "vsf.h"
#include "vsfav_usb.h"

#include "component/usb/class/common/UAC/vsfusb_UAC.h"

static uint8_t *vsfav_usb_get_entity_desc(uint8_t *desc, uint32_t desc_len,
		uint8_t id, uint8_t type)
{
	struct uavc_entity_descriptor_header *ed;

	while (desc_len > 0)
	{
		if (vsfusbh_get_extra_descriptor(desc, desc_len, USB_DT_CS_INTERFACE, (void **)&ed))
			return NULL;

		desc_len -= (uint8_t *)ed - desc;
		desc_len -= ed->header.bLength;
		desc = (uint8_t *)ed + ed->header.bLength;

		if ((!id && (ed->header.bDescriptorSubType == type)) ||
			(id && (ed->header.bDescriptorSubType >= UAC_INPUT_TERMINAL) &&
			 	(ed->header.bDescriptorSubType <= UAC1_EXTENSION_UNIT) &&
				(ed->id == id)))
			return (uint8_t *)ed;
	}
	return NULL;
}

void *vsfav_usb_getbuf(struct vsfav_usb_entity_parser_t *parser, uint32_t size)
{
	void *result = parser->buf;
	if (result != NULL)
	{
		memset(result, 0, size);
		parser->buf += size;
	}
	parser->size += size;
	return result;
}

static void vsfav_usb_increase_entity_number(struct vsfav_usb_entity_parser_t *parser)
{
	parser->entity_number++;
	if (parser->av_dev != NULL)
		parser->av_dev->topology.num_of_entities++;
}

static vsf_err_t vsfav_usb_parse_entity_bydesc(struct vsfav_usb_entity_parser_t *parser,
		struct vsfav_entity_t *master, struct uavc_entity_descriptor_header *ed);
static vsf_err_t vsfav_usb_parse_entity_byid(struct vsfav_usb_entity_parser_t *parser,
		struct vsfav_entity_t *master, uint8_t id)
{
	if (mskarr_get(parser->idmask, id - 1))
	{
		if (parser->av_dev != NULL)
		{
			struct vsfav_entity_t *entity = vsfav_get_entity_byid(parser->av_dev, id);
			vsfav_entity_add_input(master, entity);
		}
		return VSFERR_NONE;
	}
	else
	{
		struct uavc_entity_descriptor_header *ed = (struct uavc_entity_descriptor_header *)\
				vsfav_usb_get_entity_desc(parser->desc, parser->desc_len, id, 0);
		return ed ? vsfav_usb_parse_entity_bydesc(parser, master, ed) : VSFERR_FAIL;
	}
}

static vsf_err_t vsfav_usb_parse_entity_bydesc(struct vsfav_usb_entity_parser_t *parser,
		struct vsfav_entity_t *master, struct uavc_entity_descriptor_header *ed)
{
	struct vsfav_dev_t *av_dev = parser->av_dev;
	struct vsfav_entity_t *entity;
	struct vsfav_input_t *input;

	mskarr_set(parser->idmask, ed->id - 1);
	entity = (struct vsfav_entity_t *)vsfav_usb_getbuf(parser, sizeof(*entity));
	if (parser->op.parse_desc(parser, entity, ed))
		return VSFERR_FAIL;
	if (entity && av_dev)
	{
		if ((entity->type == VSFAV_INPUT) || (entity->type == VSFAV_STREAM_INPUT))
		{
			entity->next = av_dev->topology.input_entities;
			av_dev->topology.input_entities = entity;
			av_dev->topology.num_of_inputs++;
		}
		else if ((entity->type == VSFAV_OUTPUT) || (entity->type == VSFAV_STREAM_OUTPUT))
		{
			entity->next = av_dev->topology.output_entities;
			av_dev->topology.output_entities = entity;
			av_dev->topology.num_of_outputs++;
		}
		av_dev->topology.entities[parser->entity_number] = entity;
	}
	vsfav_usb_increase_entity_number(parser);

	if (master) vsfav_entity_add_input(master, entity);
	if (parser->entity.input_number)
	{
		uint8_t input_number = parser->entity.input_number;
		if (input = (struct vsfav_input_t *)vsfav_usb_getbuf(parser, sizeof(*input) + sizeof(entity) * input_number))
		{
			entity->input = input;
			input->number = input_number;
		}

		for (uint8_t i = 0; i < input_number; i++)
			if (vsfav_usb_parse_entity_byid(parser, entity, parser->entity.input_IDs[i]))
				return VSFERR_FAIL;
	}
	return VSFERR_NONE;
}

vsf_err_t vsfav_usb_generate_topology(struct vsfav_usb_entity_parser_t *parser)
{
	struct uac1_ac_header_descriptor *uac_header;
	struct uavc_entity_descriptor_header *ed;
	uint32_t tmplen;
	uint8_t *ptr;

	uac_header = (struct uac1_ac_header_descriptor *)vsfav_usb_get_entity_desc(
			parser->desc, parser->desc_len, 0, UAC_HEADER);
	if (!uac_header) return VSFERR_FAIL;

	tmplen = parser->desc_len;
	tmplen -= (uint8_t *)uac_header - parser->desc;
	ptr = (uint8_t *)uac_header;
	while (tmplen > 0)
	{
		ed = (struct uavc_entity_descriptor_header *)vsfav_usb_get_entity_desc(
				ptr, tmplen, 0, UAC_OUTPUT_TERMINAL);
		if (!ed) break;

		tmplen -= (uint8_t *)ed - (uint8_t *)ptr;
		tmplen -= ed->header.bLength;
		ptr = (uint8_t *)ed + ed->header.bLength;

		if (vsfav_usb_parse_entity_bydesc(parser, NULL, ed))
			return VSFERR_FAIL;
	}
	return VSFERR_NONE;
}

void vsfav_usb_parser_init(struct vsfav_usb_entity_parser_t *parser)
{
	parser->entity_number = 0;
	memset(parser->idmask, 0, sizeof(parser->idmask));
}
